﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;

namespace VirtualizingTreeView.Control
{
	[TemplateVisualState( GroupName = ArrowStates, Name = ArrowMouseOverState )]
	[TemplateVisualState( GroupName = ArrowStates, Name = ArrowNormalState )]
	[TemplateVisualState(GroupName = ExpanderStates, Name = ExpandedState)]
	[TemplateVisualState( GroupName = ExpanderStates, Name = CollapsedState )]
	[TemplateVisualState( GroupName = SelectionStates, Name = SelectedState )]
	[TemplateVisualState( GroupName = SelectionStates, Name = UnselectedState )]
	[TemplateVisualState( GroupName = CommonStates, Name = MouseOverState )]
	[TemplateVisualState( GroupName = CommonStates, Name = NormalState)]
	[TemplatePart(Name = ExpanderPart, Type = typeof(UIElement))]
	[TemplatePart(Name = ToggleButtonPart, Type = typeof(ToggleButton))]
	public class VirtualizingTreeItem : ContentControl
	{
		private const String ArrowStates = "ArrowStates";
		private const String ArrowMouseOverState = "ArrowMouseOver";
		private const String ArrowNormalState = "ArrowNormal";

		private const String ExpanderStates = "ExpanderStates";
		private const String ExpandedState = "Expanded";
		private const String CollapsedState = "Collapsed";

		private const String SelectionStates = "SelectionStates";
		private const String SelectedState = "Selected";
		private const String UnselectedState = "Unselected";

		private const String CommonStates = "CommonStates";
		private const String MouseOverState = "MouseOver";
		private const String NormalState = "Normal";

		private const String ExpanderPart = "Expander";
		private const String ToggleButtonPart = "ToggleButton";

		private UIElement _expander;
		private ToggleButton _toggleButton;
	
		public static readonly DependencyProperty ExpanderVisibilityProperty = DependencyProperty.Register(
			"ExpanderVisibility",
			typeof (Visibility),
			typeof (VirtualizingTreeItem),
			new PropertyMetadata( Visibility.Visible ) );

		public static readonly DependencyProperty ExpandedContentTemplateProperty = DependencyProperty.Register(
			"ExpandedContentTemplate",
			typeof (DataTemplate),
			typeof (VirtualizingTreeItem),
			null );

		public Visibility ExpanderVisibility
		{
			get { return ( Visibility ) GetValue( ExpanderVisibilityProperty ); }
			set { SetValue( ExpanderVisibilityProperty, value );}
		}

		public DataTemplate ExpandedContentTemplate
		{
			get { return ( DataTemplate ) GetValue( ExpandedContentTemplateProperty ); }
			set { SetValue( ExpandedContentTemplateProperty, value );}
		}

		internal VirtualizingDataContainer DataContainer
		{
			get { return ( VirtualizingDataContainer ) Content; }
		}

		public VirtualizingTreeItem()
		{
			DefaultStyleKey = typeof (VirtualizingTreeItem);	
		}

		public override void OnApplyTemplate()
		{
			_expander = (UIElement)GetTemplateChild( ExpanderPart );
			if(_expander != null)
			{
				_expander.MouseLeftButtonDown += OnExpanderMouseLeftButtonDown;
				_expander.MouseEnter += OnExpanderMouseEnter;
				_expander.MouseLeave += OnExpanderMouseLeave;
			}

			_toggleButton = ( ToggleButton ) GetTemplateChild( ToggleButtonPart );
			if(_toggleButton != null)
			{
				_toggleButton.Click += OnToggleButtonClick;
			}

			UpdateVisualStates( false );
		}

		protected override void OnContentChanged( Object oldContent, Object newContent )
		{
			if( newContent != null )
			{
				if( !( newContent is VirtualizingDataContainer ) )
				{
					throw new InvalidOperationException("Content is not a VirtualizedDataContainer");
				}
			}

			UpdateVisualStates();
		}

		protected override void OnMouseEnter( MouseEventArgs e )
		{
			VisualStateManager.GoToState( this,  MouseOverState, true );
		}

		protected override void OnMouseLeave( MouseEventArgs e )
		{
			VisualStateManager.GoToState( this, NormalState, true );
		}

		protected override void OnMouseLeftButtonDown( MouseButtonEventArgs e )
		{
			Select();
		}

		private void OnToggleButtonClick( Object sender, RoutedEventArgs e )
		{
			Select( _toggleButton.IsChecked );
		}

		private void OnExpanderMouseLeftButtonDown( Object sender, MouseButtonEventArgs e)
		{
			e.Handled = true;
			ExpandOrCollapse();
		}

		private void OnExpanderMouseEnter( Object sender, MouseEventArgs e )
		{
			VisualStateManager.GoToState( this, ArrowMouseOverState, true );
		}

		private void OnExpanderMouseLeave( Object sender, MouseEventArgs e )
		{
			VisualStateManager.GoToState( this, ArrowNormalState, true );
		}

		internal void Select(Boolean? yes = true)
		{
			if(DataContainer.IsSelected == yes)
			{
				return;
			}

			DataContainer.IsSelected = yes;

			VirtualizingTree tree = GetParentTree();
			if(DataContainer.IsSelected == true)
			{
				tree.OnSelectInternal( DataContainer );	
			}
			else if(DataContainer.IsSelected == false)
			{
				tree.OnUnselectInternal( DataContainer );
			}
			else if(DataContainer.IsSelected == null)
			{
				tree.OnUnknownStateInternal( DataContainer );
			}

			UpdateVisualStates();
		}

		internal void ExpandOrCollapse()
		{
			DataContainer.IsExpanded = !DataContainer.IsExpanded;
			
			VirtualizingTree tree = GetParentTree();
			if(DataContainer.IsExpanded)
			{
				tree.OnExpandedInternal( DataContainer );
			}
			else
			{
				tree.OnCollapsedInternal( DataContainer );
			}

			UpdateVisualStates();
		}

		private VirtualizingTree GetParentTree()
		{
			DependencyObject parent = VisualTreeHelper.GetParent( this );
			while( parent != null && !( parent is VirtualizingTree ) )
			{
				parent = VisualTreeHelper.GetParent( parent );
			}

			return ( VirtualizingTree )parent;
		}

		private void UpdateVisualStates( Boolean useTransitions = true )
		{
			if( DataContainer != null )
			{
				VisualStateManager.GoToState( this, DataContainer.IsExpanded ? ExpandedState : CollapsedState,
				                              useTransitions );

				VisualStateManager.GoToState( this, DataContainer.IsSelected == true ? SelectedState : UnselectedState,
				                              useTransitions );
				if ( _toggleButton != null )
				{
					_toggleButton.IsChecked = DataContainer.IsSelected;
				}
			}
			else
			{
				VisualStateManager.GoToState( this, CollapsedState, false );
				VisualStateManager.GoToState( this, UnselectedState, false );
				if( _toggleButton != null )
				{
					_toggleButton.IsChecked = null;
				}
			}
		}
	}
}
